package io.gitee.zhangbinhub.acp.cloud.oauth.authentication

import cn.dev33.satoken.context.model.SaRequest
import cn.dev33.satoken.oauth2.SaOAuth2Manager
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception
import cn.dev33.satoken.oauth2.granttype.handler.SaOAuth2GrantTypeHandlerInterface
import cn.dev33.satoken.stp.StpUtil
import cn.dev33.satoken.stp.parameter.SaLoginParameter
import cn.dev33.satoken.util.SaResult
import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.cloud.oauth.constant.OauthConstant
import io.gitee.zhangbinhub.acp.cloud.oauth.vo.TokenUserInfoVo
import io.gitee.zhangbinhub.acp.cloud.resource.server.constant.AcpCloudResourceServerConstant
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import org.bouncycastle.util.encoders.Base64
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component

@Component
class UserPasswordGrantTypeHandler(private val objectMapper: ObjectMapper) : SaOAuth2GrantTypeHandlerInterface {
    override fun getHandlerGrantType(): String = OauthConstant.granterUserPassword

    @Throws(WebException::class, SaOAuth2Exception::class)
    override fun getAccessToken(req: SaRequest, clientId: String, scopes: MutableList<String>): AccessTokenModel {
        val username: String = req.getParamNotNull(SaOAuth2Consts.Param.username)
        val password: String = req.getParamNotNull(SaOAuth2Consts.Param.password)
        val user = this.loginByUsernamePassword(clientId, username, password)
        val loginId = StpUtil.getLoginIdDefaultNull()
        if (loginId == null) {
            throw SaOAuth2Exception("登录失败")
        } else {
            val ra = RequestAuthModel()
            ra.clientId = clientId
            ra.loginId = loginId
            ra.scopes = scopes
            return SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true) { atm ->
                atm.grantType = OauthConstant.granterUserPassword
                atm.extraData[AcpCloudResourceServerConstant.TOKEN_CLAIMS_USER_INFO] = encryptUserInfo(
                    TokenUserInfoVo(
                        appId = clientId,
                        id = user.id,
                        loginNo = user.loginNo,
                        name = user.name,
                        mobile = user.mobile,
                        loginTime = System.currentTimeMillis()
                    )
                )
                atm.extraData[AcpCloudResourceServerConstant.TOKEN_CLAIMS_PERMISSION] =
                    mutableListOf("system", "test-info")
                atm.extraData[AcpCloudResourceServerConstant.TOKEN_CLAIMS_ROLE] =
                    mutableListOf("ADMIN", "TEST")
            }
        }
    }

    @Throws(WebException::class)
    private fun loginByUsernamePassword(clientId: String, name: String, password: String): TokenUserInfoVo {
        try {
            if ("user" == name && "user" == password) {
                StpUtil.logout(name, clientId)
                StpUtil.login(
                    name, SaLoginParameter()
                        .setTimeout(86400)// 单位秒，需和RefreshTokenTimeout一致
                        .setDeviceType(clientId)
                )
                SaResult.ok()
                return TokenUserInfoVo(id = name, appId = clientId, loginNo = name)
            } else {
                throw SaOAuth2Exception("账号名或密码错误")
            }
        } catch (e: Exception) {
            throw WebException(HttpStatus.UNAUTHORIZED, e.message)
        }
    }

    private fun encryptUserInfo(tokenUserInfoVo: TokenUserInfoVo) =
        Base64.toBase64String(
            objectMapper.writeValueAsString(tokenUserInfoVo).toByteArray(CommonTools.getDefaultCharset())
        )
}